package com.spzx.cart.service.impl;

import com.spzx.cart.domain.CartInfo;
import com.spzx.cart.service.ICartService;
import com.spzx.common.core.constant.CacheConstants;
import com.spzx.common.core.constant.SecurityConstants;
import com.spzx.common.core.context.SecurityContextHolder;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.domain.ProductSku;
import com.spzx.product.domain.SkuPrice;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Package: com.spzx.cart.service.impl
 *
 * @author xiongkunYang
 * @description:
 * @since 2025/6/4 17:16
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ICartServiceImpl implements ICartService {

    private final RedisTemplate<Object, Object> redisTemplate;

    private final RemoteProductService remoteProductService;

    @Override
    public void addToCart(Long skuId, Integer skuNum) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();

        // 1.构建“用户”购物车hash结构key  user：用户ID：cart
        String cartKey = CacheConstants.USER_CART_KEY + userId;
        //2.创建Hash结构绑定操作对象（方便对hash进行操作）
        BoundHashOperations<Object, Object, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);

        //3.判断用户购物车中是否包含该商品 如果包含：数量进行累加(某件商品数量上限99) 不包含：新增购物车商品
        String hashKey = skuId.toString();
        if (Boolean.TRUE.equals(hashOps.hasKey(hashKey))) {
            //3.1 说明该商品在购物车中已有，对数量进行累加 ，不能超过指定上限99
            CartInfo cartInfo = hashOps.get(hashKey);
            Assert.notNull(cartInfo, "购物车商品不存在");
            int totalNum = cartInfo.getSkuNum() + skuNum;
            cartInfo.setSkuNum(Math.min(totalNum, 99));
            hashOps.put(hashKey, cartInfo);
        } else {
            // 3.2 说明购物车没有该商品，构建购物车对象，存入Redis
            // 判断购物车商品种类（不同SKU）总数大于50件
            Long size = hashOps.size();
            size = size == null ? 0 : size;
            if (++size > 50) {
                throw new ServiceException("商品种类数量超过上限！");
            }

            CartInfo cartInfo = new CartInfo();
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            cartInfo.setSkuNum(Math.min(skuNum, 99));
            cartInfo.setCreateTime(new Date());
            cartInfo.setUpdateTime(new Date());

            // 远程调用商品服务获取商品sku基本信息
            R<ProductSku> productSkuResult = remoteProductService.getProductSku(skuId, SecurityConstants.INNER);
            if (R.FAIL == productSkuResult.getCode()) {
                throw new ServiceException(productSkuResult.getMsg());
            }
            ProductSku productSku = productSkuResult.getData();
            cartInfo.setSkuName(productSku.getSkuName());
            cartInfo.setThumbImg(productSku.getThumbImg());

            // 远程调用商品服务获取商品实时价格
            R<SkuPrice> skuPriceResult = remoteProductService.getSkuPrice(skuId, SecurityConstants.INNER);
            if (R.FAIL == skuPriceResult.getCode()) {
                throw new ServiceException(skuPriceResult.getMsg());
            }
            SkuPrice skuPrice = skuPriceResult.getData();
            cartInfo.setCartPrice(skuPrice.getSalePrice());
            cartInfo.setSkuPrice(skuPrice.getSalePrice());

            // 将购物车商品存入Redis
            hashOps.put(hashKey, cartInfo);

        }

    }

    @Override
    public List<CartInfo> getCartList() {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = CacheConstants.USER_CART_KEY + userId;
        BoundHashOperations<Object, Object, CartInfo> hashOps = this.redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = hashOps.values();
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            // 降序排序
            cartInfoList = cartInfoList.stream()
                    .sorted((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime()))
                    .toList();
            // 获取skuId列表
            List<Long> skuIdList = cartInfoList.stream().map(CartInfo::getSkuId).toList();

            // 查询商品的实时价格
            Map<Long, BigDecimal> skuIdToPriceMap = this.getSkuPriceMap(skuIdList);
            // 将SkuPrice最新价格赋值给CartInfo
            cartInfoList.forEach(cartInfo -> cartInfo.setSkuPrice(skuIdToPriceMap.get(cartInfo.getSkuId())));
            return cartInfoList;
        }
        return List.of();
    }

    @Override
    public void deleteCart(Long skuId) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = CacheConstants.USER_CART_KEY + userId;
        //获取缓存对象
        BoundHashOperations<Object, Object, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        hashOperations.delete(skuId.toString());
    }

    @Override
    public void checkCart(Long skuId, Integer isChecked) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = CacheConstants.USER_CART_KEY + userId;
        //获取缓存对象
        BoundHashOperations<Object, Object, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        String key = skuId.toString();
        if (Boolean.TRUE.equals(hashOperations.hasKey(key))) {
            CartInfo cartInfo = hashOperations.get(key);
            Assert.notNull(cartInfo, "购物车商品不存在");
            cartInfo.setIsChecked(isChecked);
            // 更新缓存
            hashOperations.put(key, cartInfo);
        }

    }

    @Override
    public void allCheckCart(Integer isChecked) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = CacheConstants.USER_CART_KEY + userId;
        //获取缓存对象
        BoundHashOperations<Object, Object, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = hashOps.values();
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            cartInfoList.forEach(cartInfo -> {
                cartInfo.setIsChecked(isChecked);
                hashOps.put(cartInfo.getSkuId().toString(), cartInfo);
            });
        }
    }

    @Override
    public void clearCart() {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = CacheConstants.USER_CART_KEY + userId;
        //获取缓存对象
        redisTemplate.delete(cartKey);
    }

    @Override
    public List<CartInfo> getCartCheckedList(Long userId) {
        String cartKey = CacheConstants.USER_CART_KEY + userId;
        BoundHashOperations<Object, Object, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = hashOps.values();
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            cartInfoList = cartInfoList.stream()
                    .filter(cartInfo -> cartInfo.getIsChecked() == 1)
                    .toList();
            return cartInfoList;
        }
        return new ArrayList<>();
    }

    /**
     * 更新用户购物车中商品的价格
     * 此方法从Redis中获取用户的购物车信息，根据商品ID获取最新的价格信息，并更新购物车中的商品价格
     *
     * @param userId 用户ID，用于获取对应的购物车信息
     * @return 返回一个布尔值，表示价格是否更新成功 如果购物车为空或没有需要更新的价格，则返回false；否则返回true
     */
    @Override
    public Boolean updateCartPrice(Long userId) {
        // 根据用户ID获取购物车的Redis键
        String cartKey = CacheConstants.USER_CART_KEY + userId;
        // 获取Redis中购物车的信息
        BoundHashOperations<Object, Object, CartInfo> hashOps = this.redisTemplate.boundHashOps(cartKey);
        // 获取购物车中的所有商品信息
        List<CartInfo> cartInfoList = hashOps.values();
        // 如果购物车为空，则无需更新价格，返回false
        if (CollectionUtils.isEmpty(cartInfoList)) {
            return false;
        }
        // 获取购物车中所有商品的SKU ID列表
        List<Long> skuIdList = cartInfoList.stream().map(CartInfo::getSkuId).toList();
        // 根据SKU ID列表获取最新的商品价格信息
        Map<Long, BigDecimal> skuPriceMap = this.getSkuPriceMap(skuIdList);
        // 遍历购物车中的商品，更新价格
        cartInfoList.forEach(cartInfo -> {
            // 获取商品的最新价格
            BigDecimal skuPrice = skuPriceMap.get(cartInfo.getSkuId());
            // 如果商品被选中且价格有变动，则更新价格，并删除缓存中的旧价格信息
            if (cartInfo.getIsChecked() == 1 && cartInfo.getSkuPrice().compareTo(skuPrice) != 0) {
                cartInfo.setSkuPrice(skuPrice);
                this.redisTemplate.delete(CacheConstants.PRODUCT_SKU_KEY + cartInfo.getSkuId());
            }
        });

        // 价格更新成功，返回true
        return true;
    }

    @Override
    public Boolean deleteCartCheckedList(Long userId) {
        // 根据用户ID获取购物车的Redis键
        String cartKey = CacheConstants.USER_CART_KEY + userId;
        // 获取Redis中购物车的信息
        BoundHashOperations<Object, Object, CartInfo> hashOps = this.redisTemplate.boundHashOps(cartKey);
        // 获取购物车中的所有商品信息
        List<CartInfo> cartInfoList = hashOps.values();
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            cartInfoList.stream()
                    .filter(cartInfo -> cartInfo.getIsChecked() == 1)
                    .forEach(cartInfo -> hashOps.delete(cartInfo.getSkuId().toString()));
        }
        return true;
    }


    /**
     * 获取SKU价格映射表
     * 通过调用远程产品服务获取指定SKU ID列表的价格信息，并将其整理为一个便于查询的映射表
     *
     * @param skuIdList SKU ID列表，用于指定需要查询价格的SKU
     * @return 返回一个映射表，键为SKU ID，值为该SKU的销售价格
     * @throws ServiceException 如果获取价格信息失败，则抛出服务异常
     */
    private Map<Long, BigDecimal> getSkuPriceMap(List<Long> skuIdList) {
        // 调用远程产品服务获取SKU价格列表
        R<List<SkuPrice>> skuPriceListResult = this.remoteProductService.getSkuPriceList(skuIdList, SecurityConstants.INNER);
        // 检查调用结果，如果失败则抛出异常
        if (R.FAIL == skuPriceListResult.getCode()) {
            throw new ServiceException(skuPriceListResult.getMsg());
        }
        // 获取价格列表数据
        List<SkuPrice> skuPriceList = skuPriceListResult.getData();
        // 将价格列表转换为映射表，便于后续快速查询
        return skuPriceList.stream()
                .collect(Collectors.toMap(SkuPrice::getSkuId, SkuPrice::getSalePrice));
    }

}